﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Operators;
using Weazel.Badger.Vhdl.Statements;

#endregion

namespace Weazel.Badger.Vhdl.Expressions
{
  public class UnaryExpression : Expression
  {
    private Expression expression;
    private UnaryFunction @operator;
    private UnaryTypeResolver resolver;    

    #region Properties    
    public Expression Expression
    {
      get
      {
        return expression;
      }

      set
      {
        expression = value;
      }
    }
    
    public UnaryFunction Operator
    {
      get
      {
        return @operator;
      }

      set
      {
        @operator = value;

        if (value == null)
          throw new NullReferenceException();
      }
    }
    #endregion

		public override void SetActualType(Entity entity, Process process, Statements.StatementCollection statements, Vhdl.Types.Type type)
    {
      if (resolved)
      {
        if (this.Type != type)
          throw new Exception();

        return;
      }


      if (resolver == null)
        throw new TypeResolveException("GetPossibleTypes() not called");

      TypeSet possibleTypes = resolver.ComputePossibleResultTypes();
      System.Diagnostics.Debug.Assert(possibleTypes.Contains(type));

      expression =
			ConversionBuilder.ApplyConversion(
				resolver.GetConversions(type.TypeId).ChildConversions, 
				(Statements.StatementCollection)ParentContainer,
				entity, 
				process,
				expression
			);

			expression.SetActualType(entity, process, statements, Types.Types.CreateType(resolver.GetConversions(type.TypeId).Map.Child, type.Width, false));

      this.Type = type;

      resolved = true;
    }

    public override bool VerifyType(ref List<VerifyMessage> messages)
    {
      if (this.Type == Types.Types.Undefined)
      {
        messages.Add(new VerifyMessage(string.Format("no type set for unary expression '{0}'", ToString())));
        return false;
      }

      verified = true;
      return true;
    }

    public override TypeSet GetPossibleTypes()
    {
      TypeSet childSet = expression.GetPossibleTypes();

      if (childSet.IsEmpty())
        throw new TypeResolveException("child type set is empty");

      resolver = 
        new UnaryTypeResolver(
          childSet, 
          expression.GezelType,
          GezelType,
          @operator.TypeMap
        );

      TypeSet result = resolver.ComputePossibleResultTypes();

      if (result.IsEmpty())
        throw new TypeResolveException("No possible types");

      return result;
    }

    protected UnaryExpression(
			IComponent parentContainer,
			Expression child, 
			Vhdl.Types.Type type, 
			GezelType gezelType
		) 
			: base(parentContainer, type, gezelType)
    {
      this.Expression = child;     
    }

    public UnaryExpression(
			IComponent parentContainer,
			UnaryFunction @operator, 
			Expression child, 
			Vhdl.Types.Type type, 
			GezelType gezelType
		) : 
      this(parentContainer, @operator, child, type, gezelType, null)
    {
      // empty 
    }

    public UnaryExpression(
			IComponent parentContainer,
      UnaryFunction @operator, 
      Expression child, 
      Vhdl.Types.Type type,
      GezelType gezelType,
      Gezel.Model.Expression expr
    ) 
      : base(parentContainer, type, gezelType, expr)
    {
      this.@operator = @operator;
      this.Expression = child;

      if (@operator == null)
        throw new NullReferenceException();
    }
 
    /// <summary>
    /// Returns a string representation of this unary expression
    /// </summary>
    /// <returns>string representation of this unary expression</returns>
    public override string ToString()
    {
      return @operator.ToString(expression.ToString()); 
    }    
  }
}
